#!/usr/bin/awk -f
function indent() {
	if(close_indent) {
		print ".RE"
		close_indent = 0
	} else {
		print ".RS"
		close_indent = 1
	}
}
function make_date(d) {
	gsub("[ \t,]+", "-", d)
	return d;
}

function trigger_header(hdate, hsect, htitle) {
	if(hdate) date = make_date(hdate)
	if(hsect) sect = hsect
	if(htitle) title = htitle
	if(sect && date && title)
		print ".TH", title, sect, date
}
# turns something like aa "b c" d "e f g"
# into a 0-indexed array with the members(aa, b c, d, e f g)
# returns length of array
function unquotewords(wordsstr, ao,    l,a,out,inpara,start,i) {
	l = split(wordsstr, a, "")
	start = 1
	out = 0
	inpara = 0
	for(i = 1; i<=l; i++) {
		if(a[i] == " ") {
			if(start == i) print "ERR"
			if(!inpara) {
				ao[out++] = substr(wordsstr, start, i-start);
				start = i + 1
			}
		} else if(a[i] == "\"") {
			if(inpara) {
				ao[out++] = substr(wordsstr, start, i-start);
				start = i + 2
				i++
			} else {
				if(start != i) print "err"
				start++;
			}
			inpara = !inpara;
		}
	}
	if(i > start) ao[out++] = substr(wordsstr, start, i-start);
	return out;
}

function arr2str(arr, start, len,    i,str) {
	for(i=start;i<=len;i++) if(i in arr) str=str arr[i] " ";
	return str
}

function cfunc(str,   i,l,a,out,restore) {
	l = unquotewords(str, a)
	# first array index, 0, contains macro name still so we skip it
	#print arr2str(a, 0, l)
	if(a[l-1] == ",") {
		restore = a[l-1];
		l--;
	}
	for(i = 0; i < l; i++) {
		out = out a[i]
		if(i == 0) out = out " "
		else if(i == 1) out = out "("
		if (i == l - 1) out = out ")"
		else if(i > 1) out = out ", "
	}
	if(restore) out = out restore
	return out;
}
function brify(str,appendnl,   temp) {
	str = ".BR \"" substr(str, index(str, " ")+1) "\""
	if(appendnl) str = str "\n"
	return str
}
function italify(str,appendnl,   temp) {
	str = ".I \"" substr(str, index(str, " ")+1) "\""
	if(appendnl) str = str "\n"
	return str
}
function fields(start, last,    buf,i,sp) {
	buf = ""
	for(i=start;i<=last;i++) {
		sp = i+1<=last ? " " : ""
		buf = buf $i sp
	}
	return buf
}
function update_var(var_name, complete_line) {
	if(!current_var) {
		current_var = var_name
		groff_vars[current_var] = substr(complete_line, 4+length(var_name)+2)
	} else {
		groff_vars[current_var] = groff_vars[current_var] complete_line
	}
	if(groff_vars[current_var] ~ /\\$/) {
		sub(/\\$/, "\n", groff_vars[current_var])
	} else {
		current_var = ""
	}
}

function replace_vars(line,   var, p) {
	# match(s,r) test whether s contains a substring matched by r
	# return index or 0, sets RSTART and RLENGTH
	while((p = match(line, /\\\*\(([A-Za-z0-9_]+)/))) {
		var = substr(line, RSTART+3, RLENGTH-3)
		if(!var in groff_vars) print "ERROR: $var not in groff_vars"
		if(groff_vars[var] ~ /\\\*\(([A-Za-z0-9_]+)/) {
			print "ERROR: groff variable substitution attempts infite loop"
			exit 1
		}
		line = substr(line, 0, RSTART-1) groff_vars[var] substr(line, RSTART+RLENGTH)
	}
	return line
}
BEGIN {
	current_var = ""
}

{
	if($1 ~ /^\./ && close_indent) {
		#indent()
	}
	
	if(!current_var)
		$0 = replace_vars($0)

	if(0) ;
	else if(current_var) {
		#process multiline groff string vars.
		update_var(current_var, $0)
		$0 = ""
# mdoc commands
# http://web.archive.org/web/20140327172811/http://mdocml.bsd.lv/mdoc.7.html
	} else if($1 == ".Bl") {
		$0 = ""
		$0 = ".sp\n.RS\n.nf\n"
		$0 = ".sp\n.RS\n"
		blockmode = 1
	} else if ($1 == ".El") {
		$0 = ""
		$0 = ".fi\n.RE"
		$0 = ".RE"
		blockmode = 0
		#indent()
	} else if($1 == ".Bd") {
		# code block - dump directly until we hit .Ed
		$0 = ".sp\n.RS\n.nf\n\\fB"
	} else if($1 == ".Ed") {
		$0 = "\\fP\n.fi\n.RE"
	} else if($1 == ".Ex") {
		locname = NF==3?$3:name
		$0 = "The \n.BR " locname "\nutility exits 0 on success, and >0 if an error occurs.\n"
	} else if($1 == ".Dd") {
		$0 = substr($0, 5)
		trigger_header($0, "", "")
		$0 = ""
	} else if($1 == ".Dt") {
		trigger_header("", $3, $2)
		$0 = ""
	} else if($1 == ".Dq") {
		if ($NF == "." || $NF == ",") {
			safe = $NF;
			$0 = substr($0, 0, length($0) - 2)
		} else safe = ""
		$0 = "“" substr($0, 5) "”" safe
	} else if($1 == ".Dv") {
		sub(/^\.Dv /, "")
	} else if($1 == ".Xr") {
		$0 = $2 "(" $3 ")"
	} else if($1 == ".An") {
		if($2 ~ "^-") $0 = ""
		else $0 = substr($0, 5)
	} else if($1 == ".In") {
		$0 = "\n.BR \"#include <" $2 ">\"\n"
	} else if($1 == ".Ft") {
		$1 = ".I"
	} else if($1 == ".Fn") {
		$0 = brify(cfunc($0), $NF != ",")
	} else if($1 == ".Fo") {
		fnblock = $0
		$0 = ""
	} else if($1 == ".Fa") {
		if(fnblock != "") {
			fnblock = fnblock " " substr($0, 5)
			$0 = ""
		} else {
			l = unquotewords(substr($0, 5), foo)
			$0 = ".XX "
			for(i=0;i<l;i++) {
				sp = i+1<l?", ":"";
				$0 = $0 foo[i] sp
			}
			$0 = italify($0, 0)
		}
	} else if($1 == ".Fc") {
		$0 = brify(cfunc(fnblock), 1)
		fnblock = ""
	} else if($1 == ".Cm") {
		out = ".Cm "
		for(i = 2; i <= NF; i+=2)
			out = out $i " "
		$0 = brify(out, 0)
	} else if($1 == ".Ic") {
		$0 = brify($0, 0)
	} else if($1 == ".Tn" || $1 == ".Va" || $1 == ".Fx") {
		$0 = substr($0, 5)
	} else if($1 == ".Po") {
		$1 = "("
	} else if($1 == ".Pc") {
		$1 = ")"
	} else if($1 == ".Pq") {
		out = ""
		for(i = 2; i <= NF; i++)
			if(!(length($i) == 2 && $i ~ /[A-Z][a-z]/))
				out = out $i " "
		if(substr(out, length(out)) == " ")
			out = substr(out, 0, length(out) -1)
		$0 = "(“" out "”)"
	} else if($1 == ".Pf") {
		$0 = $2 $4
	} else if($1 == ".Pp") {
		$0 = ".sp\n"
	} else if($1 == ".Sm") {
		spacing = $2=="on"?1:0 #unused
		$0 = ""
	} else if($1 == ".Oo" || $1 == ".Oc") {
		# denotes .Op block start/end - ignore
		$0 = ""
	} else if($1 == ".It" || $1 == ".Op") {
		if(blockmode && $1 == ".It" && $2 != "Pa") print "\n"
		#if(blockmode) indent_after=1
		out = $1 == ".Op" ? "[" : "";
		for(i = 2; i <= NF; i++) {
			if(i == 2 && $i == "Fn" && $1 == ".It") {
				print ".sp"
				out = ".Fn " substr($0, 8)
				out = brify(cfunc(out),1)
				indent_after = 1
				break;
			} else if (i == 2 && $i == "Pa" && $1 == ".It") {
				sub(/^\.It Pa/, ".I")
				out = $0 "\n.br"
				break
			} else if (i == 2 && $i == "Ev" && $1 == ".It") {
				# ignore
			} else if (i == 2 && $i == "Xo") {
				out = ""
				break
			} else if($i == "Fl" || $i == "Ar" || $i == "Cm") {
				if($i == "Fl") str = "BR -"
				else if($i == "Ar") str = "I "
				else str = "BR "
				i++;
				if(out == "") { nl = "" } else { nl = "\n" }
				out = out nl "." str $i;
				if($i == "Fl") while($(i+1) == "|") {
					out = out " | -" $(i+2)
					i += 2;
				}
			} else {
				sp = i+1<=NF?" ":""
				out = out $i sp;
			}
		}
		if($1 == ".Op") out = out "\n]"
		if(blockmode && $1 == ".It") out = out "\n"
		$0 = out;
	} else if($1 == ".Ar") {
		$1 = ".I"
		$(NF+1) = "\n.br"
	} else if($1 == ".Fl") {
		#if(blockmode) indent()
		$1 = ".BR"
		for(i = 2; i <= NF; i++) {
			$i = "-" $i
		}
	} else if($1 == ".Nd") {
		$1 = "-"
	} else if($1 == ".Nm") {
		if(name == "" && NF == 2) {
			name = $2
			$0 = ".Nm"
		}
		sep = NF > 1 ? "\n" : ""
		$0 = ".BR \"" name "\"" sep fields(2, NF)
	} else if($1 == ".At") {
		$0 = "AT&T UNIX"
	} else if($1 == ".Bx") {
		$0 = "BSD"
	} else if($1 == ".Nx") {
		$0 = "NetBSD"
	} else if($1 == ".Ux") {
		$0 = "UNIX"
	} else if($1 == ".St") {
		if(0) ;
		else if($2 == "-p1003.1-88")
			$0 = "IEEE Std 1003.1-1988 (“POSIX.1”)"
		else if($2 == "-p1003.1-90")
			$0 = "IEEE Std 1003.1-1990 (“POSIX.1”)"
		else if($2 == "-p1003.1-96")
			$0 = "ISO/IEC 9945-1:1996 (“POSIX.1”)"
		else if($2 == "-p1003.1-2001")
			$0 = "IEEE Std 1003.1-2001 (“POSIX.1”)"
		else if($2 == "-p1003.1-2004")
			$0 = "IEEE Std 1003.1-2004 (“POSIX.1”)"
		else if($2 == "-p1003.1-2008")
			$0 = "IEEE Std 1003.1-2008 (“POSIX.1”)"
		else if($2 == "-p1003.1")
			$0 = "IEEE Std 1003.1 (“POSIX.1”)"
		else if($2 == "-p1003.1b")
			$0 = "IEEE Std 1003.1b (“POSIX.1”)"
		else if($2 == "-p1003.1b-93")
			$0 = "IEEE Std 1003.1b-1993 (“POSIX.1”)"
		else if($2 == "-p1003.1c-95")
			$0 = "IEEE Std 1003.1c-1995 (“POSIX.1”)"
		else if($2 == "-p1003.1g-2000")
			$0 = "IEEE Std 1003.1g-2000 (“POSIX.1”)"
		else if($2 == "-p1003.1i-95")
			$0 = "IEEE Std 1003.1i-1995 (“POSIX.1”)"
		else if($2 == "-p1003.2-92")
			$0 = "IEEE Std 1003.2-1992 (“POSIX.2”)"
		else if($2 == "-p1003.2a-92")
			$0 = "IEEE Std 1003.2a-1992 (“POSIX.2”)"
		else if($2 == "-p1387.2-95")
			$0 = "IEEE Std 1387.2-1995 (“POSIX.7.2”)"
		else if($2 == "-p1003.2")
			$0 = "IEEE Std 1003.2 (“POSIX.2”)"
		else if($2 == "-p1387.2")
			$0 = "IEEE Std 1387.2 (“POSIX.7.2”)"
		else if($2 == "-isoC")
			$0 = "ISO/IEC 9899:1990 (“ISO C90”)"
		else if($2 == "-isoC-90")
			$0 = "ISO/IEC 9899:1990 (“ISO C90”)"
		else if($2 == "-isoC-amd1")
			$0 = "ISO/IEC 9899/AMD1:1995 (“ISO C90, Amendment 1”)"
		else if($2 == "-isoC-tcor1")
			$0 = "ISO/IEC 9899/TCOR1:1994 (“ISO C90, Technical Corrigendum 1”)"
		else if($2 == "-isoC-tcor2")
			$0 = "ISO/IEC 9899/TCOR2:1995 (“ISO C90, Technical Corrigendum 2”)"
		else if($2 == "-isoC-99")
			$0 = "ISO/IEC 9899:1999 (“ISO C99”)"
		else if($2 == "-isoC-2011")
			$0 = "ISO/IEC 9899:2011 (“ISO C11”)"
		else if($2 == "-iso9945-1-90")
			$0 = "ISO/IEC 9945-1:1990 (“POSIX.1”)"
		else if($2 == "-iso9945-1-96")
			$0 = "ISO/IEC 9945-1:1996 (“POSIX.1”)"
		else if($2 == "-iso9945-2-93")
			$0 = "ISO/IEC 9945-2:1993 (“POSIX.2”)"
		else if($2 == "-ansiC")
			$0 = "ANSI X3.159-1989 (“ANSI C89”)"
		else if($2 == "-ansiC-89")
			$0 = "ANSI X3.159-1989 (“ANSI C89”)"
		else if($2 == "-ansiC-99")
			$0 = "ANSI/ISO/IEC 9899-1999 (“ANSI C99”)"
		else if($2 == "-ieee754")
			$0 = "IEEE Std 754-1985"
		else if($2 == "-iso8802-3")
			$0 = "ISO 8802-3: 1989"
		else if($2 == "-iso8601")
			$0 = "ISO 8601"
		else if($2 == "-ieee1275-94")
			$0 = "IEEE Std 1275-1994 (“Open Firmware”)"
		else if($2 == "-xpg3")
			$0 = "X/Open Portability Guide Issue 3 (“XPG3”)"
		else if($2 == "-xpg4")
			$0 = "X/Open Portability Guide Issue 4 (“XPG4”)"
		else if($2 == "-xpg4.2")
			$0 = "X/Open Portability Guide Issue 4, Version 2 (“XPG4.2”)"
		else if($2 == "-xpg4.3")
			$0 = "X/Open Portability Guide Issue 4, Version 3 (“XPG4.3”)"
		else if($2 == "-xbd5")
			$0 = "X/Open Base Definitions Issue 5 (“XBD5”)"
		else if($2 == "-xcu5")
			$0 = "X/Open Commands and Utilities Issue 5 (“XCU5”)"
		else if($2 == "-xsh5")
			$0 = "X/Open System Interfaces and Headers Issue 5 (“XSH5”)"
		else if($2 == "-xns5")
			$0 = "X/Open Networking Services Issue 5 (“XNS5”)"
		else if($2 == "-xns5.2")
			$0 = "X/Open Networking Services Issue 5.2 (“XNS5.2”)"
		else if($2 == "-xns5.2d2.0")
			$0 = "X/Open Networking Services Issue 5.2 Draft 2.0 (“XNS5.2D2.0”)"
		else if($2 == "-xcurses4.2")
			$0 = "X/Open Curses Issue 4, Version 2 (“XCURSES4.2”)"
		else if($2 == "-susv2")
			$0 = "Version 2 of the Single UNIX Specification"
		else if($2 == "-susv3")
			$0 = "Version 3 of the Single UNIX Specification"
		else if($2 == "-svid4")
			$0 = "System V Interface Definition, Fourth Edition (“SVID4”)"
	} else if($1 == ".Pa" || $1 == ".Em") {
		$0 = italify($0, 0)
	} else if($1 == ".Bk" || $1 == ".Ek" || $1 == ".Xc" || $1 == ".Os") {
		# comments go here
		$0 = "\n"
	} else if($1 == ".D1" || $1 == ".Dl") {
		$1 = "\t"
	} else if($1 == ".Ev") {
		$0 = brify($0, 0)
	} else if($1 == ".Aq") {
		cut=4
		if(NF > 2 && length($2)==2) cut+=3
		$0 = substr($0, cut)
		$1 = "<" $1 ">"
	} else if($1 == ".Qq") {
		$0 = "\"" $2 "\"" fields(3, NF)
	} else if($1 == ".Sq") {
		$0 = "'" $2 "'" fields(3, NF)
	} else if($1 == ".Sx" || $1 == ".Sy") {
		$0 = brify($0, 0)
	} else if($1 == ".Sh") {
		$1 = ".SH"
	} else if($1 == ".Ss") {
		$1 = ".SS"
# xman stuff starts here
	} else if($1 == ".ZN") {
		$0 = italify($0, 0)
# groff stuff starts here
# http://web.cecs.pdx.edu/~trent/gnu/groff/groff.html
# lowercase stuff supported by man.c:
# nh hy nf fi sp br bp ad na ta 
	} else if($1 == ".if") { #single if block
		$0 = ""
	} else if($1 == ".ie") { #if else branch starting
		$0 = ""
	} else if($1 == ".el") { #else block
		$0 = ""
	} else if($1 == ".ds") { #set a string variable.
		update_var($2, $0)
		$0 = ""
	} else if($1 == ".ds" ||
	          $1 == ".de" ||
	          $1 == ".ce" ||
	          $1 == ".ll" ||
	          $1 == ".in" ||
	          $1 == "." ||
	          $1 == ".." ||
	          $1 == "..." ||
	          $1 == ".\\\\$" ||
	          $1 == ".\\\"" ||
	          $1 == ".ft" ||
	          $1 == ".ps" ||
	          $1 == ".ny0" ||
	          $1 == ".nr" ||
	          $1 == ".ns" ||
	          $1 == ".ne" ||
	          0) { #other groff junk
		$0 = ""
	} else if($1 == ".KS" ||
	          $1 == ".DE" ||
	          $1 == ".TA" ||
	          $1 == ".TB" ||
	          $1 == ".KE" ||
	          $1 == ".D" ||
	          $1 == ".R" ||
	          $1 == ".Sp" ||
	          $1 == ".Ve" ||

		0) { #other unknown stuff
		$0 = ""
# perl stuff starts here
# http://www.opensource.apple.com/source/perl/perl-24.1/perl/lib/Pod/Man.pm
	} else if($1 == ".Vb") { #perl verbose section starts
		$0 = ".sp\n.RS\n.nf\n"
	} else if($1 == ".Ve") { #perl verbose section ends
		$0 = ".fi\n.RE\n"
	} else if($1 == ".tr" ||
	          $1 == ".el\\" ||
	          $1 == ".rm" ||
	          $1 == ".\\}" ||
		0) { #other perl junk
		$0 = 0
	}
	print;
	if(indent_after) {
		#indent()
		indent_after = 0
	}
}

